import { filter as _filter, forEach as _forEach, result as _result, map as _map } from 'lodash';
import { FilterConfig } from '../api';
import { TypeUtils } from './type';

// NEED_DO: value未处理数组情况！！ 改为管道
// @dynamic
export class FilterUtils {
  static nullValue = [undefined, null, ''];

  static endsWith<T>(data: T[], filter: FilterConfig): T[] {
    return this.common(data, filter, (sourceValue: string, targetValue: string): boolean =>
      sourceValue.endsWith(targetValue),
    );
  }

  static contains<T>(data: T[], filter: FilterConfig): T[] {
    return this.common(data, filter, (sourceValue: string, targetValue: string): boolean =>
      sourceValue.includes(targetValue),
    );
  }

  static startsWith<T>(data: T[], filter: FilterConfig): T[] {
    return this.common(data, filter, (sourceValue: string, targetValue: string): boolean =>
      sourceValue.startsWith(targetValue),
    );
  }

  static equals<T>(data: T[], filter: FilterConfig): T[] {
    // ??是否需要类型判断
    return this.common(
      data,
      filter,
      (sourceValue: string, targetValue: string): boolean => sourceValue === targetValue,
    );
  }

  // 区间 number
  static rangeIn<T>(data: T[], filter: FilterConfig): T[] {
    const { value } = filter;
    if (!value) {
      return data;
    }
    if (typeof value !== 'object') {
      return [];
    }
    const { begin, end } = value;
    const filterBegin = parseFloat(begin);
    const filterEnd = parseFloat(end);

    return _filter(data, (item): boolean => {
      const sourceItem = _result(item, filter.key);
      const val = parseFloat(sourceItem);

      if (!begin && end) {
        return filterEnd >= val;
      }
      if (!end && begin) {
        return val >= filterBegin;
      }

      return filterEnd >= val && val >= filterBegin;
    });
  }

  static in<T>(data: T[], filter: FilterConfig): T[] {
    // ??是否需要类型判断
    return _filter(data, (item): any => {
      if (!filter || !filter.value.length) {
        return true;
      }
      let sourceItem = item;
      if (typeof item === 'object') {
        sourceItem = _result(item, filter.key);
      }
      if (FilterUtils.nullValue.includes(sourceItem)) {
        return false;
      }
      let sourceValue = sourceItem.toString();
      let targetValue = filter.value;
      if (!filter.caseSensitive) {
        sourceValue = sourceValue.toLowerCase();
        targetValue = targetValue.map((value): string => value.toString().trim().toLowerCase());
      }
      return targetValue.includes(sourceValue);
    });
  }

  static common<T>(data: T[], filter: FilterConfig, compareFn: (s: string, t: string) => number | boolean): T[] {
    return _filter(data, (item): number | boolean => {
      if (
        FilterUtils.nullValue.includes(filter.value) ||
        (typeof filter.value === 'string' && filter.value.trim() === '')
      ) {
        return true;
      }
      let sourceItem = item;
      if (typeof item === 'object') {
        sourceItem = _result(item, filter.key);
      }
      if (FilterUtils.nullValue.includes(sourceItem)) {
        return false;
      }
      let sourceValue = sourceItem.toString();
      let targetValue = filter.value.toString().trim();
      if (!filter.caseSensitive) {
        sourceValue = sourceValue.toLowerCase();
        targetValue = targetValue.toLowerCase();
      }
      return compareFn?.(sourceValue, targetValue);
    });
  }

  // 时间
  static timeIn<T>(data: T[], filter: FilterConfig): T[] {
    const { value } = filter;
    if (!value) {
      return data;
    }
    if (typeof value !== 'object') {
      return [];
    }
    let beginTime;
    let endTime;
    // 新增数组类型，配合datepicker组件使用，datepicker组件的返回值为数组类型，[开始时间，结束时间]
    if (TypeUtils.isObject(value)) {
      beginTime = value.begin;
      endTime = value.end;
    } else if (TypeUtils.isArray(value)) {
      beginTime = value[0];
      endTime = value[1];
    }

    const filterBegin = new Date(beginTime);
    const filterEnd = new Date(endTime);

    return _filter(data, (item): boolean => {
      const sourceItem = _result(item, filter.key);
      const val = new Date(sourceItem);

      if (!beginTime && endTime) {
        return filterEnd >= val;
      }
      if (!endTime && beginTime) {
        return val >= filterBegin;
      }

      return filterEnd >= val && val >= filterBegin;
    });
  }
}

export function updateDataByKey(source: any, target: any, key: string): void {
  const flatKey = key.split('.');
  let _source: any = source;
  _forEach(flatKey, (item, index): void => {
    if (index === flatKey.length - 1) {
      _source[item] = target;
      return;
    }
    _source = _source[item];
  });
}

// @dynamic
export class TreeFilterUtils extends FilterUtils {
  static clone<T>(data: T[], childrenKey = 'children'): T[] {
    return _map(data, (item): any => {
      const temp = { ...item };

      const children = _result(item, childrenKey);
      if (children) {
        temp[childrenKey] = this.clone(children, childrenKey);
      }
      return temp;
    });
  }

  static filter<T>(
    data: T[],
    filter: FilterConfig,
    customFilterFn: (...args: any[]) => any = null,
    childrenKey = 'children',
  ): T[] {
    const filterTool = (item, config): any[] => {
      let status = [];
      const defaultValue = [1];

      if (customFilterFn) {
        const temp = customFilterFn(item, config);

        if (typeof temp === 'boolean') {
          if (temp) {
            status = defaultValue;
          }
        } else {
          status = temp;
        }
      } else {
        status = this[filter.filterMode]([item], config);
      }
      return status;
    };

    const traversalFilter = (source: T[], config: FilterConfig): any =>
      _filter(source, (item): any => {
        // 自己或子
        const temp = filterTool(item, config);
        if (temp.length) {
          return temp;
        }
        if (!childrenKey) {
          return false;
        }
        const childrenTemp = traversalFilter(_result(item, childrenKey), config);
        updateDataByKey(item, childrenTemp, childrenKey);
        return childrenTemp.length;
      });

    return traversalFilter(data, filter);
  }
}
